/**
 * @brief 演示c++11 thread和Linux pthread如何配合使用
 * @note 本示例演示了以下功能：
     1. 通过线程名，查找指定线程的id，即tid
     2. 使用pthread_kill，获取某一线程的函数调用栈
     注意事项：
     1. 在子线程中调用pthread_self和在其他线程中调用thread::native_handle的结果相同
     2. pthread_kill的第一个参数不能是Linux中的tid，而必须是glibc中的pthread_t
     3. 子线程对象不能调用thread::detach，否则thread::native_handle返回的结果为0。此时只有把子线程中调用pthread_self，把结果保存下来，给pthread_kill使用
     4. 子线程对象调用过thread::detach后，其他线程仍然可以通过pthread_kill操作子线程
     5. 编译时要加-rdynamic选项，例如g++ a.cpp --std=c++11 -lpthread -rdynamic
 */

#include <iostream>
#include <thread>
#include <sys/prctl.h>
#include <sys/signal.h>
#include <fcntl.h> 
#include <unistd.h>  
#include <stdio.h>  
#include <dirent.h>
#include <string.h>  
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h> 
#include <execinfo.h>

void sighand(int signo)  
{
    int j, nptrs;
    void *buffer[1024];
    char **strings;
    nptrs = backtrace(buffer, 1024);
    strings = backtrace_symbols(buffer, nptrs);
    if (strings == NULL) {
        perror("Backtrace:");
        exit(EXIT_FAILURE);
    }
    for (j = 0; j < nptrs; j++)
        printf("%s\n", strings[j]);
    free(strings);
}  

int GethTIDFromName(const char* find_name)
{  
	const char* dir = "/proc/self/task";
    DIR *dp;
    struct dirent *entry;
    struct stat statbuf;
	char cmd_buf[100] = {0};
	char thread_name[20] = {0};
	int res = -1;

    if((dp = opendir(dir)) == NULL)
    {  
        puts("can't open dir.");  
        return res;
    }  
    chdir (dir);
    while((entry = readdir(dp)) != NULL)
    {  
        lstat(entry->d_name, &statbuf);
        if(S_IFDIR &statbuf.st_mode)
        {  
            if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)  
              continue;  
  
			sprintf(cmd_buf, "%s/comm", entry->d_name);
			FILE* pfile = fopen(cmd_buf, "r");
			fscanf(pfile, "%s", thread_name);
			if (strcmp(thread_name, find_name) == 0)
			{
				res = std::stoi(entry->d_name);
				break;
			}
        }
    }  
    chdir("..");
    closedir(dp);

	return res;
}  

void f1(void)
{
	while (1)
	{

	}
}

void f2(void)
{
	f1();
}

void f3(void)
{
	f2();
}

void f4(void)
{
	prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("subthread"), 0, 0, 0);
	std::cout<< "pthread_self = " << pthread_self() << std::endl;
	f3();
}

int main(void)
{
	struct sigaction        actions;  
	memset(&actions, 0, sizeof(actions));  
        sigemptyset(&actions.sa_mask);
        actions.sa_flags = 0;  
        actions.sa_handler = sighand;  
        sigaction(SIGALRM, &actions, NULL);  

	std::thread t(f4);
	std::this_thread::sleep_for(std::chrono::seconds(5));

	pthread_t tid = t.native_handle();

	int ttid = GethTIDFromName("subthread");
	std::cout<< tid << " " << ttid << std::endl;
	std::string cmd = "cat /proc/" + std::to_string(ttid) + "/comm";
	system(cmd.c_str());

	pthread_kill(tid, SIGALRM);
	
	t.join();
}

